// 获取 canvas 对象
const canvas = document.getElementById('main-canvas');

// 获取 gl context 对象
const gl = getWebGLContext(canvas);

// 加载 gl 扩展
const requiredWebglExtensions = [
  'EXT_shader_texture_lod',
  'OES_standard_derivatives',
  'OES_element_index_uint',
  'OES_texture_float',
  'OES_texture_float_linear'
];

loadWebGlExtensions(gl, requiredWebglExtensions);

// 获取 shader
const vertShaderContent = `
attribute vec3 a_Position;
attribute vec2 a_UV;

uniform mat4 u_ViewProjectionMatrix;

varying vec2 v_UV;

void main()
{
  gl_Position = u_ViewProjectionMatrix * vec4(a_Position, 1.0);
  v_UV = a_UV;
}
`;
const fragShaderContent = `
precision highp float;

uniform sampler2D u_Sampler;

varying vec2 v_UV;

void main()
{
  vec4 textureColor = texture2D(u_Sampler, v_UV);
  gl_FragColor = vec4(vec3(textureColor), 1.0);
}
`;

// 编译 shader 成 program
const vertShader = compileVertShader(gl, vertShaderContent);
const fragShader = compileFragShader(gl, fragShaderContent);
const program = linkProgram(gl, vertShader, fragShader);

// 设置相机
const camera = new Camera();

// 鼠标绑定事件对象属性
const mouseEventProp = {
  canvas: canvas,
  mouseDown: false,
  pressedButton: undefined,
  lastMouseX: 0,
  lastMouseY: 0,
  orignMouseX: 0,
  orignMouseY: 0,
  onClick: (x, y) => {},
  onRotate: (x, y) => {
    camera.rotate(x, y);
  },
  onPan: (x, y) => {
    camera.pan(x, y);
  },
  onZoom: (delta) => {
    camera.zoomIn(delta);
  }
};

// 绑定鼠标
document.addEventListener('mouseup', mouseUpHandler.bind(mouseEventProp), { passive: true });
document.addEventListener('mousemove', mouseMoveHandler.bind(mouseEventProp), { passive: true });
canvas.addEventListener('mousedown', mouseDownHandler.bind(mouseEventProp), { passive: true });
canvas.addEventListener('wheel', mouseWheelHandler.bind(mouseEventProp), { passive: true });

// 屏蔽右键菜单
document.oncontextmenu = () => false;

// 准备
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.colorMask(true, true, true, true);
gl.clearDepth(1.0);
gl.cullFace(gl.FRONT);

// 地图相关函数
const quadCache = {};

function prepareQuads(gl, renderQuads, tileXY, currentLevel, globalWidth, globalHeight, range = 3) {
  const xTiles = getNumberOfXTilesAtLevel(currentLevel);
  const yTiles = getNumberOfYTilesAtLevel(currentLevel);

  for (let i = Math.max(tileXY.x - range, 0), leni = Math.min(tileXY.x + range, xTiles - 1); i <= leni; i++) {
    for (let j = Math.max(tileXY.y - range, 0), lenj = Math.min(tileXY.y + range, yTiles - 1); j <= lenj; j++) {
      // 摄像机所在位置tile 的key
      this.prepareQuad(gl, renderQuads, i, j, currentLevel, globalWidth, globalHeight);
    }
  }
}

function prepareQuad(gl, renderQuads, x, y, currentLevel, globalWidth, globalHeight) {
  const cameraPosKey = `${x}-${y}-${currentLevel}`;

  if (quadCache[cameraPosKey] === undefined) {
    quadCache[cameraPosKey] = new Quad(gl, x, y, currentLevel, [globalWidth, globalHeight]);
  }

  renderQuads.push(quadCache[cameraPosKey]);
}

camera.fitViewToScene();
camera.updatePosition();

// 渲染帧
function renderFrame(ms) {
  window.requestAnimationFrame(renderFrame);

  // 全局宽度和高度
  const globalWidth = canvas.clientWidth;
  const globalHeight = canvas.clientHeight;
  canvas.width = globalWidth;
  canvas.height = globalHeight;
  gl.viewport(0, 0, globalWidth, globalHeight);

  // 计算摄像机位置
  camera.aspectRatio = globalWidth / globalHeight;
  camera.updatePosition();

  // 计算投影矩阵视图矩阵
  const projMatrix = camera.projectionMatrix();
  const viewMatrix = camera.viewMatrix();
  const viewProjectionMatrix = glMatrix.mat4.create();
  glMatrix.mat4.multiply(viewProjectionMatrix, projMatrix, viewMatrix);

  // 计算摄像机中心点于地平面的交点
  const origin = camera.position;
  const direction = glMatrix.vec3.create();

  glMatrix.vec3.subtract(direction, camera.target, origin);
  glMatrix.vec3.normalize(direction, direction);

  const normal = glMatrix.vec3.clone([0.0, 1.0, 0.0]);
  const distance = 0.0;

  const result = rayIntersectionWidthPlane([origin, direction], [normal, distance]);

  if (result === undefined) {
    // 没有交点或者角度不对
    return;
  }

  // 获取当前层级 界定最大和最小层级
  const currentLevel = Math.min(
    Math.max(0, 
      getCurrentLevel(camera, [globalWidth, globalHeight], result)),
    11
  );

  // 计算摄像机射线于地面交点的经纬度弧度
  const rayLonlatRadians = tileWorld2lonlatRadians([result[0], result[2]]);
  // 计算摄像机所在位置的 tile x y
  const tileXY = radiansToTileXY(rayLonlatRadians[0], rayLonlatRadians[1], currentLevel);
  // 准备渲染quad列表
  const renderQuads = [];
  prepareQuads(gl, renderQuads, tileXY, currentLevel, globalWidth, globalHeight);

  // 清理 颜色和深度缓冲
  gl.clearColor(66.0 / 255.0,66.0 / 255.0, 66.0 / 255.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  // 使用当前shader
  gl.useProgram(program);

  // 绘制
  for (let i = 0, len = renderQuads.length; i < len; i++) {
    const quadBuf = renderQuads[i];

    // 如果没有准备好 则不渲染
    if (quadBuf.status !== 1) {
      return ;
    }

    // 更新 uniform 值
    const viewProjectionMatrixLoc = gl.getUniformLocation(program, 'u_ViewProjectionMatrix');
    gl.uniformMatrix4fv(viewProjectionMatrixLoc, false, viewProjectionMatrix);

    // 更新 attr 值
    const positionLoc = setAttributeBuffer(gl, program, 'a_Position', quadBuf.quadBufferVertices, 3);
    const uvLoc = setAttributeBuffer(gl, program, 'a_UV', quadBuf.quadBufferUV, 2);

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadBuf.quadBufferIndices);

    gl.activeTexture(gl.TEXTURE0);

    gl.bindTexture(gl.TEXTURE_2D, quadBuf.texture);

    if (!quadBuf.textureInit) {
      gl.texImage2D(gl.TEXTURE_2D, 0,
        gl.RGBA, gl.RGBA,
        gl.UNSIGNED_BYTE, quadBuf.image);

      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // NEAREST
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); // LINEAR
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      quadBuf.textureInit = true;
    }

    const loc = gl.getUniformLocation(program, 'u_Sampler');

    gl.uniform1i(loc, 0);

    // gl.enable(gl.CULL_FACE);
    gl.disable(gl.CULL_FACE);
    gl.enable(gl.DEPTH_TEST);
    gl.disable(gl.BLEND);

    gl.drawElements(4, 6, 5123, 0);

    gl.disableVertexAttribArray(positionLoc);
    gl.disableVertexAttribArray(uvLoc);
  }
}

// 启动渲染帧
window.requestAnimationFrame(renderFrame);

// 取消遮罩
const spinner = document.getElementById('mask');

if (spinner !== undefined) {
  spinner.className = 'mask-none';
  setTimeout(() => {
    spinner.style.display = 'none';
  }, 1000);
}
